<?php
/*
 * Copyright (c) 2013 Eugene Kuzmenko
 *
 * Permission  is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in  the Software without restriction, including without limitation the rights
 * to  use,  copy,  modify,  merge, publish, distribute, sublicense, and/or sell
 * copies  of  the  Software,  and  to  permit  persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS  FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * For more information, see <http://www.aljey.com>.
 */

namespace Al\HTTP\Lib\Vars;

use Al\Core\Lib\ParameterHolder,
    Al\Core\Lib\Service;

/**
 * Provides an array-like wrapper for $_SESSION.
 * Optimizes the session storage performance by utilizing the framework cache
 * instead of the default php file handler.
 * 
 * @author Eugene Kuzmenko <mail@aljey.com>
 */
class Session extends ParameterHolder {

  /**
   * @var string Session id.
   */
  protected $id;

  /**
   * @inheritdoc
   * Automatically stores request parameters which have a name that starts with
   * an underscore.
   */
  public function __construct() {
    if (ini_get('session.auto_start')) @session_write_close();
    session_set_save_handler(array($this, 'open'), array($this, 'close'),
      array($this, 'read'), array($this, 'write'), array($this, 'destroy'),
      array($this, 'gc'));
    register_shutdown_function('session_write_close');
    session_start();
    parent::__construct($_SESSION);
    foreach (Service::get('http.params') as $key => $value) {
      if ($key[0] == '_') $this[$key] = $value;
    }
    if (!$this['_locale']) {
      $this['_locale'] = Service::get('config.locale', 'en');
    }
  }

  /** Session is always started successfully. */
  public function open($savePath, $sessionName) { return true; }

  /** Session is always ended successfully. */
  public function close() { return true; }

  /**
   * Returns encoded session data by id.
   *
   * @param string $id
   * @return string
   */
  public function read($id) {
    $this->id = $id;
    return Service::get("cache.session.$id.data", '');
  }

  /**
   * Saves encoded session data by id.
   *
   * @param string $id
   * @param string $data
   * @return bool
   */
  public function write($id, $data) {
    $cache = Service::get('cache');
    $session = $cache['session'] ? $cache['session'] : array();
    if (!array_key_exists($id, $session)) {
      $session[$id] = array('time' => time());
    }
    $session[$id]['data'] = $data;
    $cache['session'] = $session;
    return true;
  }

  /**
   * Destroys session data by id.
   *
   * @param string $id
   * @return bool
   */
  public function destroy($id) {
    $cache = Service::get('cache');
    $session = $cache['session'] ? $cache['session'] : array();
    unset($session[$id]);
    $cache['session'] = $session;
    Service::get('event')->fire('session.remove', array($id));
    return true;
  }

  /**
   * Garbage collector.
   *
   * @param int $maxlifetime
   * @return bool
   */
  function gc($maxlifetime) {
    $cache = Service::get('cache');
    $session = $cache['session'] ? $cache['session'] : array();
    foreach ($session as $id => $value) {
      if ($value['time'] + $maxlifetime < time()) {
        unset($session[$id]);
        Service::get('event')->fire('session.remove', array($id));
      }
    }
    $cache['session'] = $session;
    return true;
  }

  /** @inheritdoc */
  public function offsetSet($offset, $value) {
    if (is_string($offset) && strlen($offset)) {
      $_SESSION[$offset] = $value;
      parent::offsetSet($offset, $value);
    }
  }
  
  /** @inheritdoc */
  public function offsetUnset($offset) {
    unset($_SESSION[$offset]);
    parent::offsetUnset($offset);
  }

  /**
   * Returns the session id.
   *
   * @return string
   */
  public function getId() {
    return $this->id;
  }
  
}